use std::collections::HashMap;

use common_base::{
    futures::TryStreamExt,
    prelude::*,
    sqlx::{self, mysql::MySqlArguments, Arguments, MySql},
    tokio::runtime::Handle,
};
use common_core::{
    data::{
        BizMetadata, DatasetOwner, DatasetType, DatasetUtil, Metadata, MetadataType, RowsetOwner,
        RowsetRef,
    },
    ServiceContextBase,
};

use crate::MetadataManager;

use super::metadata_util::MetadataUtil;

pub struct BizMetadataManager;

impl BizMetadataManager {
    pub async fn get_metadata_model(
        &self,
        srv_ctx: &mut ServiceContextBase,
        object_id: &str,
    ) -> Result<Option<BizMetadata>> {
        // load biz_model definition
        let mut biz_metadata = self.get_sys_model(srv_ctx, object_id).await?;
        if let Some(biz_metadata) = biz_metadata.as_mut() {
            self.load_sys_mdl_val(srv_ctx, biz_metadata).await?;
            self.load_sys_mdl_ctn(srv_ctx, biz_metadata).await?;
            self.load_mdl_ctn_fcts(srv_ctx, biz_metadata).await?;
            self.load_dct_and_fctmap(biz_metadata);
            self.init_dct_metadata(srv_ctx, biz_metadata).await?;
        }
        Ok(biz_metadata)
    }

    async fn get_sys_model(
        &self,
        srv_ctx: &mut ServiceContextBase,
        object_id: &str,
    ) -> Result<Option<BizMetadata>> {
        let mut arguments = MySqlArguments::default();
        arguments.add(object_id);

        let (sql, arguments) = MetadataUtil::generate_sql_and_args(
            srv_ctx,
            BizMetadata::SYS_MODEL,
            "*",
            "MDL_ID=?",
            "",
            arguments,
            object_id,
        );

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        while let Some(row) = rows.try_next().await? {
            return Ok(Some(BizMetadata::new(object_id, row.try_into()?)));
        }
        Ok(None)
    }

    /// 装入模型扩展属性
    async fn load_sys_mdl_val(
        &self,
        srv_ctx: &mut ServiceContextBase,
        biz_metadata: &mut BizMetadata,
    ) -> Result<()> {
        if biz_metadata.get_extend_property("").is_some() {
            return Ok(());
        }

        let mut arguments = MySqlArguments::default();
        arguments.add(biz_metadata.get_object_id());

        let table_name = BizMetadata::SYS_MDL_VAL;
        let (sql, arguments) =
            DatasetUtil::generate_sql_and_args(srv_ctx, table_name, "*", "MDL_ID=?", "", arguments);

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        let mut dataset = DatasetOwner::get_instance(table_name, DatasetType::default());
        dataset.set_prime_key(vec![BizMetadata::MDL_KEY, "UNIT_ID"]);
        while let Some(row) = rows.try_next().await? {
            let rowset: RowsetOwner = row.try_into()?;
            let rowset_ref = dataset.insert_rowset(rowset);
            dataset.build_prime_key_index(&rowset_ref);
        }
        Metadata::process_extend_property(
            biz_metadata.metadata_base_mut(),
            &dataset,
            "UNIT_ID",
            "MDL_KEY",
            "MDL_VALUE",
        );
        Ok(())
    }

    /// 装入模型内容定义
    async fn load_sys_mdl_ctn(
        &self,
        srv_ctx: &mut ServiceContextBase,
        biz_metadata: &mut BizMetadata,
    ) -> Result<()> {
        if biz_metadata.get_dataset(BizMetadata::SYS_MDL_CTN).is_some() {
            return Ok(());
        }

        let mut arguments = MySqlArguments::default();
        arguments.add(biz_metadata.get_object_id());

        let table_name = BizMetadata::SYS_MDL_CTN;
        let (sql, arguments) =
            DatasetUtil::generate_sql_and_args(srv_ctx, table_name, "*", "MDL_ID=?", "", arguments);

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        let mut dataset = DatasetOwner::get_instance(table_name, DatasetType::default());
        dataset.set_prime_key(vec![BizMetadata::CTN_ID]);
        while let Some(row) = rows.try_next().await? {
            let rowset: RowsetOwner = row.try_into()?;
            let rowset_ref = dataset.insert_rowset(rowset);
            dataset.build_prime_key_index(&rowset_ref);
        }
        biz_metadata.insert_dataset("SYS_MDL_CTN", dataset);

        Ok(())
    }

    /// 装入模型内容中的事实表定义
    async fn load_mdl_ctn_fcts(
        &self,
        srv_ctx: &mut ServiceContextBase,
        biz_metadata: &mut BizMetadata,
    ) -> Result<()> {
        let biz_metadata_ref = biz_metadata as *mut BizMetadata;

        if let Some(dataset) = biz_metadata.get_dataset(BizMetadata::SYS_MDL_CTN) {
            for rowset in dataset.get_rowset_list() {
                self.process_facts(srv_ctx, biz_metadata_ref, rowset)?;
                self.process_contents(srv_ctx, biz_metadata_ref, rowset);
            }
        }

        Ok(())
    }

    fn process_facts(
        &self,
        srv_ctx: &mut ServiceContextBase,
        biz_metadata: *mut BizMetadata,
        rowset: &RowsetRef,
    ) -> Result<()> {
        let ctn_id = rowset.get_string("CTN_ID");
        if ctn_id.is_empty() {
            return Ok(());
        }

        let biz_metadata = unsafe { &mut *biz_metadata };
        // let mut fct_list: Vec<FctMetadata> = Vec::default();
        for i in 0..BizMetadata::FCT_COUNT {
            let fct_id = rowset.get_string(format!("CTN_FCT{}", i + 1).as_str());
            if fct_id.is_empty() {
                continue;
            }

            if let Some(fct_metadata) = Handle::current().block_on(
                MetadataManager::get_instance(MetadataType::Fct).get_metadata(srv_ctx, fct_id),
            )? {
                biz_metadata.add_fct_metadata(fct_metadata);
                // fct_list.push(fct_metadata);
            }
        }
        // biz_metadata.add_fct_metadata_2_ctn(ctn_id.to_owned(),fct_list);

        Ok(())
    }

    fn process_contents(
        &self,
        _srv_ctx: &mut ServiceContextBase,
        biz_metadata: *mut BizMetadata,
        rowset: &RowsetRef,
    ) {
        let biz_metadata = unsafe { &mut *biz_metadata };
        let ctn_id = rowset.get_string("CTN_ID");
        let ctn_type = rowset.get_string("CTN_TYPE");
        if ctn_id.is_empty() || ctn_type.is_empty() {
            return;
        }

        let ctn_type = match ctn_type {
            "01" => BizMetadata::BIZ_CTN_TYPE_JBDS,
            "02" => BizMetadata::BIZ_CTN_TYPE_JIDS,
            "03" => BizMetadata::BIZ_CTN_TYPE_JPDS,
            "10" => BizMetadata::BIZ_CTN_TYPE_BLDS,
            "05" => BizMetadata::BIZ_CTN_TYPE_BCDS,
            x @ _ => {
                warn!("find invalid ctn_type:{}, becareful.", x);
                x
            }
        };
        biz_metadata.add_ctn_id_2_ctn_type_map(ctn_type, ctn_id);
    }

    /// dont use pointer to point the ctx
    fn load_dct_and_fctmap(&self, biz_metadata: &mut BizMetadata) {
        // TODO: make biz_metadata's ref_dct_map/ref_fct_map point to dct_map/fct_map in ctx
    }

    /// 装入SYS_MODEL中其他的引用字典的元数据（非事实表引用的字典）
    async fn init_dct_metadata(
        &self,
        srv_ctx: &mut ServiceContextBase,
        biz_metadata: &mut BizMetadata,
    ) -> Result<()> {
        if !srv_ctx.contains_key("DCT_MAP") {
            srv_ctx.put_object("DCT_MAP", HashMap::default().into())
        }

        // 年度字典
        self.init_dct_metdata_concrete(srv_ctx, biz_metadata, "MDL_NDCT", false)
            .await?;
        // 月度字典
        self.init_dct_metdata_concrete(srv_ctx, biz_metadata, "MDL_YDCT", false)
            .await?;
        // 日度字典
        self.init_dct_metdata_concrete(srv_ctx, biz_metadata, "MDL_RDCT", false)
            .await?;
        // 类型字典
        self.init_dct_metdata_concrete(srv_ctx, biz_metadata, "MDL_BHDCT", false)
            .await?;
        // 统一类型字典
        self.init_dct_metdata_concrete(srv_ctx, biz_metadata, "MDL_TYDCT", false)
            .await?;
        // 其他附属字典，定义在扩展属性中
        self.init_dct_metdata_concrete(srv_ctx, biz_metadata, BizMetadata::AFFIX_OBJID, true)
            .await?;

        Ok(())
    }

    async fn init_dct_metdata_concrete(
        &self,
        srv_ctx: &mut ServiceContextBase,
        biz_metadata: &mut BizMetadata,
        dct_type: &str,
        is_extend: bool,
    ) -> Result<()> {
        let dct_id = if !is_extend {
            biz_metadata.get_string(dct_type).to_owned()
        } else {
            biz_metadata.get_extend_property_value(dct_type).to_owned()
        };
        if dct_id.trim().is_empty() {
            return Ok(());
        }

        for dct_id in dct_id.split(",") {
            if biz_metadata.get_dct_metadata(srv_ctx, dct_id).is_some() {
                continue;
            }

            if let Some(dct_metadata) = MetadataManager::get_instance(MetadataType::Dct)
                .get_metadata(srv_ctx, dct_id)
                .await?
            {
                biz_metadata.add_dct_metadata(srv_ctx, dct_metadata);
            }
        }
        Ok(())
    }
}
